/*
 * 2006-1-13 lxm 创建。 
 */
package com.hd123.rumba.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import com.hd123.rumba.exception.BusinessException;

/**
 * 提供多对象根据指定的属性进行比较
 * <p>
 * 
 * 给定“左”“右”两个对象数组，按照指定的属性进行比较，区分以下三种情况：<br>
 * 1) 左边有，右边没有；<br>
 * 2) 右边有，左边没有；<br>
 * 3) 左右两边都有。<br>
 * 若不指定属性名，则默认取属性"uuid"。若指定的属性不存在，则报错。若有一方属性取值为null，则始终认为两者不匹配。
 * 
 * @see java.lang.Comparable
 * 
 * @author lxm
 * 
 */
public class CompareObjects {

  private List lackRights = new ArrayList();
  private List lackLefts = new ArrayList();
  private List matchLefts = new ArrayList();
  private List matchRights = new ArrayList();

  public static CompareObjects compare(Object[] lefts, Object[] rights) throws BusinessException {
    return compare(lefts, "uuid", rights, "uuid");
  }

  public static CompareObjects compare(Object[] lefts, String leftPropName, Object[] rights)
      throws BusinessException {
    return compare(lefts, leftPropName, rights, "uuid");
  }

  public static CompareObjects compare(Object[] lefts, Object[] rights, String rightPropName)
      throws BusinessException {
    return compare(lefts, "uuid", rights, rightPropName);
  }

  /**
   * 比较操作。
   * 
   * @param lefts
   *          左边对象数组。
   * @param leftPropName
   *          左边对象的比较属性名。
   * @param rights
   *          右边对象数组。
   * @param rightPropName
   *          右边对象的比较属性名。
   * @return 比较结果，类型为<code>CompareObjects</code>
   */
  public static CompareObjects compare(Object[] lefts, String leftPropName, Object[] rights,
      String rightPropName) throws BusinessException {
    CompareObjects co = new CompareObjects();
    String leftMethodName = co.toGetPropMethodName(leftPropName);
    String rightMethodName = co.toGetPropMethodName(rightPropName);

    for (int i = 0; i < lefts.length; i++) {
      Object lobj = lefts[i];
      Object robj = null;
      int j = 0;
      Object lval = co.getPropValue(lobj, leftMethodName);
      if (lval != null) {
        // 在rights中查找匹配的对象。
        for (; j < rights.length; j++) {
          robj = rights[j];
          Object rval = co.getPropValue(robj, rightMethodName);
          if (rval == null)
            continue;
          if (lval.equals(rval))
            break;
        }
      }
      // 若找到匹配的，则加入matchLefts和matchRights；若没有找到，则加入lackRights。
      if (j < rights.length) {
        co.matchLefts.add(lobj);
        co.matchRights.add(robj);
      } else
        co.lackRights.add(lobj);
    }

    // 检查所有的rights。剩下没有被加入到matchRights中的全部缴入到lackLefts中。
    for (int i = 0; i < rights.length; i++) {
      Object robj = rights[i];
      if (co.matchRights.indexOf(robj) == -1)
        co.lackLefts.add(robj);
    }

    return co;
  }

  /**
   * 取得左边有，右面没有的对象列表。
   * 
   * @return 左边对象列表。
   */
  public List getLackRights() {
    return lackRights;
  }

  /**
   * 取得右边有，左边么有的对象列表。
   * 
   * @return 右边对象列表。
   */
  public List getLackLefts() {
    return lackLefts;
  }

  /**
   * 取得两边都有的左边对象列表。
   * 
   * @return
   */
  public List getMatchLefts() {
    return matchLefts;
  }

  /**
   * 取得两边都有的右边对象列表。
   * 
   * @return
   */
  public List getMatchRights() {
    return matchRights;
  }

  /**
   * 根据左边对象查找匹配的右边对象。
   * 
   * @param left
   *          左边对象
   * @return 返回匹配的右边对象，若没有找到返回null。
   */
  public Object leftToRight(Object left) {
    int idx = matchLefts.indexOf(left);
    try {
      return matchRights.get(idx);
    } catch (Exception e) {
      return null;
    }
  }

  /**
   * 根据右边对象查找匹配的左边对象。
   * 
   * @param right
   *          右边对象
   * @return 返回匹配的左边对象，若没有找到返回null。
   */
  public Object rightToLeft(Object right) {
    int idx = matchRights.indexOf(right);
    try {
      return matchLefts.get(idx);
    } catch (Exception e) {
      return null;
    }
  }

  private String toGetPropMethodName(String propName) {
    return "get" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
  }

  private Object getPropValue(Object source, String getMethodName) throws BusinessException {
    try {
      Method m = source.getClass().getMethod(getMethodName, (Class[]) null);
      return m.invoke(source, (Object[]) null);
    } catch (Exception e) {
      throw new BusinessException("对象" + source.getClass() + "中找不到属性方法" + getMethodName + "。");
    }
  }

}
